#pragma once

#include <iostream>
#include <memory>
#include <cstring>
#include <sys/epoll.h>
#include "InetAddr.hpp"
#include "LogMessage.hpp"
#include "Socket.hpp"
#include "Epoller.hpp"

const int defaulteventsize = 64;

class EpollServer
{
public:
    EpollServer(uint16_t port)
        : _listenSocket(new TcpSocket), _eventsize(defaulteventsize), _events(new epoll_event[_eventsize]), _timeout(-1)
    {
        _listenSocket->CreateBindListenSocket(port);
        _epoll = new Epoller;

        // 将listensocket添加进epoll模型当中
        _epoll->AddEpoll(_listenSocket->GetSockfd());
    }

    ~EpollServer()
    {
        delete[] _events;
        delete _epoll;
        close(_listenSocket->GetSockfd());
        delete _listenSocket;
    }

    void Start()
    {
        while (true)
        {
            int n = _epoll->WaitEpoll(_events, _eventsize, _timeout);
            if (n == 0)
            {
                lg.LogMessage(Debug, "time out");
            }
            else if (n < 0)
            {
                lg.LogMessage(Warning, "epoll_wait error, errno: %d, strerror: %s", errno, strerror(errno));
            }
            else
            {
                // 打印所有就绪事件
                std::cout << "events list: ";
                for (int i = 0; i < n; i++)
                {
                    std::cout << _events[i].data.fd << " ";
                }
                std::cout << std::endl;

                Handler(n);
            }
        }
    }

    void Handler(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int sockfd = _events[i].data.fd;
            uint32_t event = _events[i].events;

            if (sockfd == _listenSocket->GetSockfd())
            {
                if (event & EPOLLIN)
                {
                    InetAddr addr;
                    Socket *newsock = _listenSocket->AcceptConnect(addr);
                    _epoll->AddEpoll(newsock->GetSockfd());
                    lg.LogMessage(Normal, "get a new link, sockfd: %d", newsock->GetSockfd());
                    delete newsock;
                }
            }
            else
            {
                if (event & EPOLLIN)
                {
                    sockaddr_in addr;
                    socklen_t addrlen = sizeof(addr);
                    char buffer[4096];
                    int n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&addr, &addrlen);
                    if (n > 0)
                    {
                        buffer[n] = 0;
                        lg.LogMessage(Normal, "clent echo# %s", buffer);
                    }
                    else if (n == 0)
                    {
                        lg.LogMessage(Normal, "you close the connect, me too");
                        _epoll->DelEpoll(sockfd);
                        close(sockfd);
                    }
                    else
                    {
                        lg.LogMessage(Warning, "recvfrom error, errno: %d, strerror: %s", errno, strerror(errno));
                    }
                }
            }
        }
    }

private:
    Socket *_listenSocket;
    Epoller *_epoll;
    size_t _eventsize;
    struct epoll_event *_events;
    int _timeout;
};